<?php
/**
 * Media-handling base classes and generic functionality
 * @file
 * @ingroup Media
 */

/**
 * Base media handler class
 *
 * @ingroup Media
 */
abstract class MediaHandler
{
  const TRANSFORM_LATER = 1;
  protected

  /**
   * Instance cache
   */
  protected static
    $availableHandlers = array
                          (
	                        'image/jpeg'     => 'BitmapHandler',
	                        'image/png'      => 'BitmapHandler',
	                        'image/gif'      => 'BitmapHandler',
	                        'image/x-ms-bmp' => 'BmpHandler',
	                        'image/x-bmp'    => 'BmpHandler',
	                        'image/svg+xml'  => 'SvgHandler',  // official
	                        'image/svg'      => 'SvgHandler',  // compat
	                        'image/vnd.djvu' => 'DjVuHandler', // official
	                        'image/x.djvu'   => 'DjVuHandler', // compat
	                        'image/x-djvu'   => 'DjVuHandler', // compat
                          ),
    $handlers = array();

  /**
   * Get a MediaHandler for a given MIME type from the instance cache
   */
  public static function getHandler($type)
  {
    if (!isset(self::$availableHandlers[$type]))
    {
      //die("No handler found for $type.\n");
      return false;
    }
    $class = self::$availableHandlers[$type];
    if (!isset(self::$handlers[$class]))
    {
      self::$handlers[$class] = new $class;
      if (!self::$handlers[$class]->isEnabled())
      {
        self::$handlers[$class] = false;
      }
    }
    return self::$handlers[$class];
  }

  /**
   * Get an associative array mapping magic word IDs to parameter names.
   * Will be used by the parser to identify parameters.
   */
  abstract public function getParamMap();

  /*
   * Validate a thumbnail parameter at parse time.
   * Return true to accept the parameter, and false to reject it.
   * If you return false, the parser will do something quiet and forgiving.
   */
  abstract public function validateParam($name, $value);

  /**
   * Merge a parameter array into a string appropriate for inclusion in filenames
   */
  abstract public function makeParamString($params);

  /**
   * Parse a param string made with makeParamString back into an array
   */
  abstract public function parseParamString($str);

  /**
   * Changes the parameter array as necessary, ready for transformation.
   * Should be idempotent.
   * Returns false if the parameters are unacceptable and the transform should fail
   */
  abstract public function normaliseParams($image, $params);

  /**
   * Get an image size array like that returned by getimagesize(), or false if it
   * can't be determined.
   *
   * @param Image $image The image object, or false if there isn't one
   * @param string $fileName The filename
   * @return array
   */
  abstract public function getImageSize($filename);

  /**
   * Get handler-specific metadata which will be saved in the img_metadata field.
   *
   * @param Image $image The image object, or false if there isn't one
   * @param string $fileName The filename
   * @return string
   */
  public function getMetadata($image, $path)
  {
    return '';
  }

  /**
   * Get a string describing the type of metadata, for display purposes.
   */
  public function getMetadataType($image)
  {
    return false;
  }

  /**
   * Check if the metadata string is valid for this handler.
   * If it returns false, Image will reload the metadata from the file and update the database
   */
  public function isMetadataValid($image, $metadata)
  {
    return true;
  }


  /**
   * Get a MediaTransformOutput object representing an alternate of the transformed
   * output which will call an intermediary thumbnail assist script.
   *
   * Used when the repository has a thumbnailScriptUrl option configured.
   *
   * Return false to fall back to the regular getTransform().
   */
  public function getScriptedTransform($image, $script, $params)
  {
    return false;
  }

  /**
   * Get a MediaTransformOutput object representing the transformed output. Does not
   * actually do the transform.
   *
   * @param Image $image The image object
   * @param string $dstPath Filesystem destination path
   * @param string $dstUrl Destination URL to use in output HTML
   * @param array $params Arbitrary set of parameters validated by $this->validateParam()
   */
  public function getTransform($image, $dstPath, $dstUrl, $params)
  {
    return $this->doTransform($image, $dstPath, $dstUrl, $params, self::TRANSFORM_LATER);
  }

  /**
   * Get a MediaTransformOutput object representing the transformed output. Does the
   * transform unless $flags contains self::TRANSFORM_LATER.
   *
   * @param Image $image The image object
   * @param string $dstPath Filesystem destination path
   * @param string $dstUrl Destination URL to use in output HTML
   * @param array $params Arbitrary set of parameters validated by $this->validateParam()
   * @param integer $flags A bitfield, may contain self::TRANSFORM_LATER
   */
  abstract public function doTransform($image, $dstPath, $dstUrl, $params, $flags = 0);

  /**
   * Get the thumbnail extension and MIME type for a given source MIME type
   * @return array thumbnail extension and MIME type
   */
  public function getThumbType($extension, $mime)
  {
    return array($extension, $mime);
  }

  /**
   * True if the handled types can be transformed
   */
  public function canRender($file)
  {
    return true;
  }
  /**
   * True if handled types cannot be displayed directly in a browser
   * but can be rendered
   */
  public function mustRender($file)
  {
    return false;
  }
  /**
   * True if the type has multi-page capabilities
   */
  public function isMultiPage($file)
  {
    return false;
  }
  /**
   * Page count for a multi-page document, false if unsupported or unknown
   */
  public function pageCount($file)
  {
    return false;
  }
  /**
   * False if the handler is disabled for all files
   */
  public function isEnabled()
  {
    return true;
  }

  /**
   * Get an associative array of page dimensions
   * Currently "width" and "height" are understood, but this might be
   * expanded in the future.
   * Returns false if unknown or if the document is not multi-page.
   */
  public function getPageDimensions($image, $page)
  {
    $gis = $this->getImageSize($image, $image->getPath());
    return array
           (
             'width'  => $gis[0],
             'height' => $gis[1]
           );
  }

  /**
   * Get an array structure that looks like this:
   *
   * array(
   *    'visible' => array(
   *       'Human-readable name' => 'Human readable value',
   *       ...
   *    ),
   *    'collapsed' => array(
   *       'Human-readable name' => 'Human readable value',
   *       ...
   *    )
   * )
   * The UI will format this into a table where the visible fields are always
   * visible, and the collapsed fields are optionally visible.
   *
   * The function should return false if there is no metadata to display.
   */

  /**
   * FIXME: I don't really like this interface, it's not very flexible
   * I think the media handler should generate HTML instead. It can do
   * all the formatting according to some standard. That makes it possible
   * to do things like visual indication of grouped and chained streams
   * in ogg container files.
   */
  public function formatMetadata($image)
  {
    return false;
  }

  /**
   * @fixme document this!
   * 'value' thingy goes into a wikitext table; it used to be escaped but
   * that was incompatible with previous practice of customized display
   * with wikitext formatting via messages such as 'exif-model-value'.
   * So the escaping is taken back out, but generally this seems a confusing
   * interface.
   */
  /*protected static function addMeta($array, $visibility, $type, $id, $value, $param = false)
  {
    $array[$visibility][] = array
                            (
                              'id' => "$type-$id",
                              'name' => wfMsg( "$type-$id", $param ),
                              'value' => $value
                            );
  }

  public function getShortDesc( $file ) {
    global $wgLang;
    $nbytes = '(' . wfMsgExt( 'nbytes', array( 'parsemag', 'escape' ),
      $wgLang->formatNum( $file->getSize() ) ) . ')';
    return "$nbytes";
  }

  public function getLongDesc( $file ) {
    global $wgUser;
    $sk = $wgUser->getSkin();
    return wfMsgExt( 'file-info', 'parseinline',
      $sk->formatSize( $file->getSize() ),
      $file->getMimeType() );
  }*/

  public function getDimensionsString($file)
  {
    return '';
  }

  /**
   * Modify the parser object post-transform
   */
  public function parserTransformHook($parser, $file) {}

  /**
   * Check for zero-sized thumbnails. These can be generated when
   * no disk space is available or some other error occurs
   *
   * @param $dstPath The location of the suspect file
   * @param $retval Return value of some shell process, file will be deleted if this is non-zero
   * @return true if removed, false otherwise
   */
  public function removeBadFile($dstPath, $retval = 0)
  {
    if(file_exists($dstPath))
    {
      $thumbstat = stat($dstPath);
      if($thumbstat['size'] == 0 || $retval != 0)
      {
        unlink($dstPath);
        return true;
      }
    }
    return false;
  }
}
